﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.IO;

namespace JobShop
{
    [Description("Standard Genetic Algorithm with Gap-Reduction Technique")]
    public class TcGapReduction : BackgroundWorker
    {
        // Properties to bind.
        public TcProperties Properties { get; set; }
        public Int32 Progress { get; set; }
        public TeRunningStatus Status { get; private set; }
        public Boolean IsFinished { get; private set; }
        public TcProblem Problem { get; private set; }
        public TcStatistics Statistics { get; private set; }
        public FileInfo Input { get; private set; }
                
        // Private properties.
        private Int32 ChromSize { get; set; }
        private TcGeneration Generation { get; set; }

        // An MxN matrix where I(m,n) represents the order of n in m.
        private Int32[,] OrderOfTasks { get; set; }
        //------------------------------------------------------------------------------

        public TcGapReduction(String prmInput)
        {
            WorkerSupportsCancellation = true;
            WorkerReportsProgress = true;

            Input = new FileInfo(prmInput);
            Properties = new TcProperties();

            // Allocate the memory for the collections.
            InitializeData();

            // Initiate the random solutions.
            PreProcess();
        }
        //------------------------------------------------------------------------------

        protected override void OnDoWork(DoWorkEventArgs e)
        {
            Statistics = new TcStatistics(Properties.GenSize);
            Status = TeRunningStatus.Running;
            ProduceInitialPopulation();

            for (int i = 0; i < Properties.GenSize && !CancellationPending; i++)
            {
                Generation.Evaluate();

                // Update the statistical data to be updated on GUIs.
                UpdateStatistics();

                // Notify the observers about the changes.
                ReportProgress(Progress);

                // Reproduce a new generation given by crossover and mutation rate.
                Generation = Generation.Reproduce();
            }
            base.OnDoWork(e);
        }
        //------------------------------------------------------------------------------

        protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
        {
            Status = TeRunningStatus.Finished;
            IsFinished = true;
            Progress = 100;
            base.OnRunWorkerCompleted(e);
        }
        //------------------------------------------------------------------------------

        private void InitializeData()
        {
            ChromSize = 0;
            Status = TeRunningStatus.Created;
            IsFinished = false;
            Progress = 0;
            Statistics = null;
        }
        //------------------------------------------------------------------------------

        private void ProduceInput()
        {
            if (!Input.Exists)
                throw new FileMissingException(Input.Name);

            using (StreamReader rd = new StreamReader(Input.FullName))
            {
                try
                {
                    String[] tokens = rd.ReadLine().Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    Problem = new TcProblem(Path.GetFileNameWithoutExtension(Input.Name)
                                           , Convert.ToInt32(tokens[0])
                                           , Convert.ToInt32(tokens[1]));
                    ChromSize = Problem.M * Problem.N * (Problem.N - 1);
                    OrderOfTasks = new Int32[Problem.M, Problem.N];
                    if (!Problem.TryParse(rd.ReadToEnd()))
                        throw new Exception();
                }
                catch(Exception)
                {
                    throw new InvalidInputFileException("Incorrect input file");
                }
                finally
                {
                    rd.Close();
                }
            }
        }
        //------------------------------------------------------------------------------

        private void ProduceInitialPopulation()
        {
            Generation = new TcGeneration(1, Properties);
            Generation.CreateInitialPopulation(Problem);
        }
        //------------------------------------------------------------------------------

        private void PreProcess()
        {
            // Get the input from the input file.
            ProduceInput();
        }
        //------------------------------------------------------------------------------

        private void UpdateStatistics()
        {
            Progress = Convert.ToInt32((Convert.ToDouble(Generation.Index) / Properties.GenSize) * 100);
            Statistics.Update(Generation);
        }
        //------------------------------------------------------------------------------

        public void Reset()
        {
            InitializeData();
        }
        //------------------------------------------------------------------------------

    }
    //------------------------------------------------------------------------------

}
//------------------------------------------------------------------------------